package com.ncmmall.mall.controller.securitys.shiro;

import com.ncmmall.domain.security.auth.Authority;
import com.ncmmall.domain.security.role.Role;
import com.ncmmall.domain.security.user.WebUser;
import com.ncmmall.querychannel.service.QueryChannelService;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Sets;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;

import java.util.List;
import java.util.Set;

/**
 * @author likaikai
 */
public class ShiroRealm extends AuthorizingRealm {

    private QueryChannelService queryChannelService;

    public QueryChannelService getQueryChannelService() {
        return queryChannelService;
    }

    public void setQueryChannelService(QueryChannelService queryChannelService) {
        this.queryChannelService = queryChannelService;
    }

    /**
     * 校验是否有权限
     *
     * @param principals
     * @return
     */
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {

        if (principals == null) {
            throw new AuthorizationException("PrincipalCollection method argument cannot be null.");
        }

        String username = (String) getAvailablePrincipal(principals);

        try {

            WebUser user = queryChannelService.findOne("select u from WebUser u where u.username = :username and u.role.name = 'MERCHANT' ", ImmutableMap.of("username", username), WebUser.class);

            checkUser(user, username);

            return getSimpleAuthorizationInfo(user);

        } catch (Exception e) {
            throw translateAuthorizationException(e);
        }
    }


    /**
     * 登录的时候使用，校验用户令牌
     *
     * @param token
     * @return
     * @throws AuthenticationException
     */
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
        UsernamePasswordToken authorizationToken = (UsernamePasswordToken) token;
        String username = authorizationToken.getUsername();

        if (Strings.isNullOrEmpty(username)) {
            throw new AccountException("Null usernames are not allowed by this realm.");
        }

        try {

            WebUser user = queryChannelService.findOne("select u from WebUser u where u.username = :username and u.role.name = 'MERCHANT' ", new ImmutableMap.Builder<String, Object>().put("username", username).build(), WebUser.class);

            checkUser(user, username);

            iniUserAuthoritysInSession(user);

            return buildAuthenticationInfo(username, user.getPassword().toCharArray());

        } catch (Exception e) {
            throw translateAuthenticationException(e);
        }

    }

    /**
     * 初始化用户拥有的角色及角色所拥有的所有权限，写入session
     *
     * @param user
     */
    private void iniUserAuthoritysInSession(WebUser user) {


        List<Role> roles = queryChannelService.findAll("select r from Role  r where r.id = :id", ImmutableMap.of("id", user.getRole().getId()), Role.class);
        List<Authority> authorities = queryChannelService.findAll("select a from Authority a where a.id in (select r.authoritysId from RoleAuthority r where roleId = :roleId)", ImmutableMap.of("roleId", user.getRole().getId()), Authority.class);

        Set<String> roleNames = Sets.newHashSet();
        for (Role role : roles) {
            roleNames.add(role.getName());
        }

        Set<String> permissions = Sets.newHashSet();
        for (Authority authority : authorities) {
            permissions.add(authority.getPermission());
        }

        Securitys.getSession().setAttribute("shiro_roles", roleNames);
        Securitys.getSession().setAttribute("shiro_permission", permissions);

    }


    /**
     * 获取当前用户的所属角色以及所拥有的权限
     *
     * @param user
     * @return
     */
    private SimpleAuthorizationInfo getSimpleAuthorizationInfo(WebUser user) {

        SimpleAuthorizationInfo info = new SimpleAuthorizationInfo((Set<String>) Securitys.getSession().getAttribute("shiro_roles"));
        info.setStringPermissions((Set<String>) Securitys.getSession().getAttribute("shiro_permission"));

        return info;
    }

    /**
     * @param e
     * @return
     */
    private AuthorizationException translateAuthorizationException(Exception e) {
        if (e instanceof AuthorizationException) {
            return (AuthorizationException) e;
        }

        return new AuthorizationException(e);
    }

    /**
     * @param user
     * @param username
     */
    private void checkUser(WebUser user, String username) {
        if (null == user) {
            throw new UnknownAccountException("No account found for user [" + username + "]");
        }

        if (!user.isAccountNonLocked()) {
            throw new LockedAccountException("Account found for user [" + username + "] is locked");
        }
    }

    /**
     * @param e
     * @return
     */
    private AuthenticationException translateAuthenticationException(Exception e) {
        if (e instanceof AuthenticationException) {
            return (AuthenticationException) e;
        }

        return new AuthenticationException(e);
    }

    /**
     * @param username
     * @param password
     * @return
     */
    protected AuthenticationInfo buildAuthenticationInfo(String username, char[] password) {
        return new SimpleAuthenticationInfo(username, password, getName());
    }
}
